///////////////////////////////////////////////////////////////////////////
// Programa VIDA. 
//
// Esto no es un ejemplo de buena programacion ni esta optimizado
// en velocidad, solo tiene por proposito ilustrar sobre el algoritmo
// de VIDA. Es decir, esta pensado para ser facil de entender.
//
///////////////////////////////////////////////////////////////////////////
#include <stdio.h>             // funciones de impresion
#include <stdlib.h>            // funciones aleatorias
#include <unistd.h>            // si queremos poner delays

#define FILAS 80
#define COLS 80 

#define CELULA 1               // No modificar

#define NUM_COL COLS/30+1
#define ELEMENTOS (COLS/30+1)*FILAS	//numero de elementos del vector pantalla (que es la matriz principal, solo que expresada como vector)
#define CANTIDAD (FILAS/16+1)*NUM_COL
#define ELEM_FINAL COLS%30

int pantalla[FILAS][COLS];    // para carga inicial
int pantalla_v[ELEMENTOS];    // Pantalla del ciclo
int repetidos[CANTIDAD];	//vector de bits reproducidos

///////////////////////////////////////////////////////////////////////////
//      Inicializa la matriz de pantaala y pone las celulas iniciales
///////////////////////////////////////////////////////////////////////////
// void IniciarPantalla(void)
// {
// unsigned int col, fila;
// 
// for(col=0; col<COLS; col++)                // Exploramos todo el mundo
//      for(fila=0; fila<FILAS; fila++)
//              pantalla2[fila][col]=0;              // Pomemos el mundo a 0 (vacio)
// 
// for(int n=0; n<POBLACION_INICIAL; n++)     // Siembra celulas aleatoriamente
//      {
//      fila=random(FILAS);
//      col=random(COLS);
//      pantalla2[fila][col]=CELULA;
//      }
// }


///////////////////////////////////////////////////////////////////////////
//   Calcula si la celulas se reproducen, viven o mueren
//50--///////////////////////////////////////////////////////////////////////////

#define XSIZE (COLS/32+1)

void CicloVida(void)
{
register int col, fila;

register int up,current,low,next;
register int rep;
register int vecinos;
int buff_up;
int columnas=30;
int n_col;

columnas=30;

for(n_col=0;n_col<NUM_COL;n_col++) //BARRIDO POR COLUMNAS DE ENTEROS
{
	if(n_col==NUM_COL-1) {columnas=ELEM_FINAL;}
	fila=0;
	buff_up=pantalla_v[n_col*FILAS+0];//INT MAS ALTO EN LA MATRIZ, SE DEBE GUARDAR PARA QUE SIRVA COMO VECINO LOW DEL ULTIMO INTEGER
	
	//primer elemento de la fila
	up=pantalla_v[FILAS-1+n_col*FILAS];//ULTIMO ELEMENTO INTEGER, ULTIMA FILA
	low=pantalla_v[1+n_col*FILAS];
	current=pantalla_v[0+n_col*FILAS];
	next=current;
	rep=0;
	
	for(col=0;col<columnas;col++)//RECORRIDO DEL INTEGER POR DENTRO
	{
		vecinos=0;
		vecinos += ( (up >> col) & 1 );		
		vecinos += ( (up >> (col+1) ) & 1 );		
		vecinos += ( (up >> (col+2) ) & 1 );		

		vecinos += ( (low >> col) & 1 );
		vecinos += ( (low >> (col+1)) & 1 );
		vecinos += ( (low >> (col+2)) & 1 );

		vecinos += ( (current >> col) & 1 );
		vecinos += ( (current >> (col+2)) & 1 );//FAST COUNT OF NEIGHBOURS
		
		if(   (current & (1 << (col+1)))  ==  (1 << (col+1)))//LA CELDA ACTUAL ESTA VIVA
		{
			if(vecinos<2 || vecinos >3)//MUERE
			{
				next &= ~(1 << (col+1));
			}
		}
		else	//CELDA ACUTAL MUERTA
		{								//100
			if(vecinos == 3)//NACE
			{
				next |= (1 << (col+1));
			}
		}		
	}
	if(    (next & (1<<1))  == (1<<1) )
	{
		rep |= (1 << (2*(fila%16)));	//la parte izquierda de los bits a repetir se guardan en la parte par del integer repeticion
	}
	if(  (next & (1 << columnas))  == ( 1 << columnas )  )
	{
		rep |= (1 << (2*(fila%16) +1)); //la parte derecha de los bits a repetir se guarda en la parte impar
	}	
	pantalla_v[0+n_col*FILAS]=next;   //todavia sin acutalizar los bits extremos, eso es luego de toda la actualizacion de columnas		

	//elementos de la fila desde 1 hasta el ante ultimo
	for(fila=1;fila<FILAS-1;fila++)
	{
		up=current;
		current=low;
		low=pantalla_v[fila+1+n_col*FILAS];
		next=current;

		if(fila%16==0)
		{
			rep=0;
		}

		for(col=0;col<columnas;col++)
		{
		vecinos=0;
		vecinos += ( (up >> col) & 1 );		
		vecinos += ( (up >> (col+1) ) & 1 );		
		vecinos += ( (up >> (col+2) ) & 1 );		

		vecinos += ( (low >> col) & 1 );
		vecinos += ( (low >> (col+1)) & 1 );
		vecinos += ( (low >> (col+2)) & 1 );

		vecinos += ( (current >> col) & 1 );
		vecinos += ( (current >> (col+2)) & 1 );//FAST COUNT OF NEIGHBOURS
			
			if(   (current & (1 << (col+1))) == (1 << (col+1)))
			{
				if(vecinos<2 || vecinos >3)
				{
					next &= ~(1 << (col+1));
				}									//150
			}
			else
			{
				if(vecinos == 3)
				{
					next |= (1 << (col+1));
				}
			}
		}//fin del for col
		pantalla_v[fila+n_col*FILAS]=next;   //todavia sin acutalizar los bits extremos, eso es luego de toda la actualizacion de columnas

		if(  (next & (1<<1)) == (1<<1) )
		{
			rep |= (1 << (2*(fila%16)));	//la parte izquierda de los bits a repetir se guardan en la parte par del integer repeticion
		}
		if(  (next & (1 << columnas)) == ( 1 << columnas )  )
		{
			rep |= (1 << (2*(fila%16) +1)); //la parte derecha de los bits a repetir se guarda en la parte impar
		}			


		if(fila%16==15)
		{
			repetidos[fila/16+n_col*(FILAS/16+1)]=rep;
		}

	}//fin del for fila

	//ultimo elemento de la fila:
	up=current;
	current=low;
	low=buff_up;
	next=current;

	if(fila%16==0)
	{
		rep=0;
	}
	for(col=0;col<columnas;col++)
	{
		vecinos=0;								//200
		vecinos += ( (up >> col) & 1 );		
		vecinos += ( (up >> (col+1) ) & 1 );		
		vecinos += ( (up >> (col+2) ) & 1 );		

		vecinos += ( (low >> col) & 1 );
		vecinos += ( (low >> (col+1)) & 1 );
		vecinos += ( (low >> (col+2)) & 1 );

		vecinos += ( (current >> col) & 1 );
		vecinos += ( (current >> (col+2)) & 1 );//FAST COUNT OF NEIGHBOURS
		
		if(   (current & (1 << (col+1))) == (1 << (col+1)))
		{
			if(vecinos<2 || vecinos >3)
			{
				next &= ~(1 << (col+1));
			}
		}
		else
		{
			if(vecinos == 3)
			{
				next |= (1 << (col+1));
			}
		}	
	}
	pantalla_v[FILAS-1+n_col*FILAS]=next;   //todavia sin acutalizar los bits extremos, eso es luego de toda la actualizacion de columnas
	
	if(  (next & (1<<1)) == (1<<1) )
	{
		rep |= (1 << (2*(fila%16)));	//la parte izquierda de los bits a repetir se guardan en la parte par del integer repeticion
	}
	if(  (next & (1 << columnas)) == ( 1 << columnas )  )
	{
		rep |= (1 << (2*(fila%16) +1)); //la parte derecha de los bits a repetir se guarda en la parte impar
	}			
	repetidos[fila/16+n_col*(FILAS/16+1)]=rep;
}//final del barrido de las columnas integer.
//ahora se deben acutalizar los valores de bits izq y derecho de cada columna:

}//fin de la funcion ciclo vida

void actualizar_bits_extra()
{
	int register izq,der;
	int register fila,col;

	for(col=1;col<NUM_COL-1;col++)
	{
		for(fila=0;fila<FILAS;fila++)
		{
			if(fila%16==0)
			{
				izq=repetidos[(col-1)*(FILAS/16+1)+fila/16];
				der=repetidos[(col+1)*(FILAS/16+1)+fila/16];
			}

			if(  (izq & ( 1 << (2*(fila%16)+1) )) == ( 1 << (2*(fila%16)+1) )   )
			{
				pantalla_v[col*FILAS+fila] |= (1);
			}
			else	pantalla_v[col*FILAS+fila] &= ~(1);
			if(  (der & ( 1 << (2*(fila%16)) )) == ( 1 << (2*(fila%16)) )   )
			{
				pantalla_v[col*FILAS+fila] |= (1 << 31);
			}
			else	pantalla_v[col*FILAS+fila] &= ~(1 << 31);
		}
	}

//columna 0
	for(fila=0;fila<FILAS;fila++)
	{
		if(fila%16==0)
		{
			izq=repetidos[(NUM_COL-1)*(FILAS/16+1)+fila/16];
			der=repetidos[(1)*(FILAS/16+1)+fila/16];
		}

		if(  (izq & ( 1 << (2*(fila%16)+1) )) == ( 1 << (2*(fila%16)+1) )   )
		{
			pantalla_v[0+fila] |= (1);
		}
		else 	pantalla_v[0+fila] &= ~(1);
		if(  (der & ( 1 << (2*(fila%16)) )) == ( 1 << (2*(fila%16)) )   )
		{
			pantalla_v[0+fila] |= (1 << 31);
		}
		else	pantalla_v[0+fila] &= ~(1 << 31);
	}	
//columna ultima
	for(fila=0;fila<FILAS;fila++)
	{
		if(fila%16==0)
		{
			izq=repetidos[(NUM_COL-2)*(FILAS/16+1)+fila/16];
			der=repetidos[0+fila/16];
		}

		if(  (izq & ( 1 << (2*(fila%16)+1) )) == ( 1 << (2*(fila%16)+1) )   )
		{
			pantalla_v[(NUM_COL-1)*FILAS+fila] |= (1);
		}
		else	pantalla_v[(NUM_COL-1)*FILAS+fila] &= ~(1);
		if(  (der & ( 1 << (2*(fila%16)) )) == ( 1 << (2*(fila%16)) )   )
		{
			pantalla_v[(NUM_COL-1)*FILAS+fila] |= (1 << ELEM_FINAL+1);
		}
		else	pantalla_v[(NUM_COL-1)*FILAS+fila] &= ~(1 << ELEM_FINAL+1);
	}	
}//fin de actualizacion

///////////////////////////////////////////////////////////////////////////
//------------------ Nos despedimos y pomenos los creditos ----------------
///////////////////////////////////////////////////////////////////////////
// void DespedidaYCierre(void)
// {
// clrscr();
// cprintf("\r\n\r\n\r\n");
// cprintf("                IMPLEMENTACION DEL PROGRAMA VIDA\r\n");
// cprintf("\r\n");
// cprintf("   Bueno, esto es todo. Este programa esta en modo texto y no tiene\r\n");
// cprintf(" ninguna optimizacion en velocidad, ni siquiera la implementacion\r\n");
// cprintf(" esta pensada para ser veloz. Su unico proposito es ilustrar sobre\r\n");
// cprintf(" algoritmo del programa. De las multiples implementaciones de este\r\n");
// cprintf(" esta es seguramente la mas sencilla de entender.\r\n\r\n");
// cprintf("   Programado por SILICOn, y puesto bajo licencia GNU. Modificalo y\r\n");
// cprintf(" distribuyelo como gustes, pero no te olvides de citar la fuente.\r\n");
// cprintf("\r\n");
// cprintf("\r\n");
// cprintf("   NOTA: Es mi primera excursion en la vida artificial.\r\n");
// }

int leerDatosIniciales()
{
  int fila, col, n_col;
  char inputc;
  for(fila=0; fila<FILAS; fila++)
        {
        for(col=0; col<COLS; col++)
          {
          inputc = getchar();
          switch (inputc) {
                case ' ': pantalla[fila][col]=0;
                                  break;
                case '@': pantalla[fila][col]=CELULA;
                                  break;
                default:  fprintf(stderr, "Error en el archivo de entrada: fila %d contiene un caracter invalido en la columna %d \n", fila, col);
                                  return(-1);
                }
          }
        if(getc(stdin)!='\n')
          {
          fprintf(stderr, "Error en el archivo de entrada: fila %d no termina en \\n \n", fila);
          return(-1);
          }
        }

//Ahora se debe pasar esta matriz a la matriz que el programa ejecutara, que es una matriz bit a bit:
//si en todo este esquema se reemplazan las partes de los if's donde se pregunta por pantalla[fila][columna]==1
//por una lectura directa al archivo entonces se ahorraria la parte anterior y el uso de la matriz pantalla

//primero se la carga con ceros,proceso necesario ya que luego se escribiran solo los 1's
for(fila=0;fila<ELEMENTOS;fila++)
{
	pantalla_v[fila]=0;
}

//carga de las columnas extrema izquierda y extrema derecha:
//extrema derecha primero:

for(fila=0;fila<FILAS;fila++)
{
	for(col=1;col<ELEM_FINAL+1;col++)
	{
		if(pantalla[fila][col-1+(NUM_COL-1)*30]==1)
		{
			pantalla_v[FILAS*(NUM_COL-1)+fila] |= (1 << col);
		}
	}
//A CONTINUACION SE LE CARGAN LOS VALORES IZQUIERDO Y DERECHO REPETIDOS
	if(pantalla[fila][29+30*(NUM_COL-2)]==1)
	{
		pantalla_v[FILAS*(NUM_COL-1)+fila] |= (1);
	}
	if(pantalla[fila][0]==1)
	{
		pantalla_v[FILAS*(NUM_COL-1)+fila] |= (1 << (ELEM_FINAL+1));
	}
}

//extrema izquiera luego:

for(fila=0;fila<FILAS;fila++)
{
	for(col=1;col<31;col++)
	{
		if(pantalla[fila][col-1]==1)
		{
			pantalla_v[fila] |= (1 << col);
		}
	}
//VALORES IZQUIERDO Y DERECHO REPETIDOS. EN ESTE CASO EL IZQ ES EL TOROIDA, POR LO TANTO EL ULTIMO
	if(pantalla[fila][COLS-1]==1)
	{
		pantalla_v[fila] |= (1);
	}
	if(pantalla[fila][0+30]==1)
	{
		pantalla_v[fila] |= (1 << 31);
	}
}

//otros elementos intermedios
for(n_col=1;n_col<NUM_COL-1;n_col++)
{
for(fila=0;fila<FILAS;fila++)
{
	for(col=1;col<31;col++)
	{
		if(pantalla[fila][(col-1)+30*n_col]==1)
		{
			pantalla_v[FILAS*n_col+fila] |= (1 << col);
		}
	}
	//repeticion de valores en los extremos del integer:
	if(pantalla[fila][29+30*(n_col-1)]==1)	//elemento de la izquierda
	{
		pantalla_v[FILAS*n_col+fila] |= (1);
	}
	if(pantalla[fila][0+30*(n_col+1)]==1)	//elemento de la derecha
	{
		pantalla_v[FILAS*n_col+fila] |= (1 << 31);
	}
}
}//fin del for n_col

        return(0);
}


void grabarMatriz(int iteracion)
{
  int fila, col, n_col;
  int columnas=31;
  char filename[20];
  char s[COLS+1];
  FILE *archivo;
  int j;
  
  sprintf(filename, "out/salida%03d.txt",iteracion);
  archivo = fopen(filename, "w");

for(fila=0;fila<FILAS;fila++)
{
  for(n_col=0;n_col<NUM_COL; n_col++)   
        {
		if(n_col==NUM_COL-1)
		{
			columnas=ELEM_FINAL+1;
		}
		else columnas=31;

		for(col=1;col<columnas;col++)
		{
			if(  (pantalla_v[FILAS*n_col+fila] & (1 << col)) == (1 << col))
			{
				fputc('@',archivo);
			}
			else	{fputc(' ',archivo);}
		}
	}
	fputc('\n',archivo);
}
		
  fclose(archivo);
}

///////////////////////////////////////////////////////////////////////////
// La funcion principal
///////////////////////////////////////////////////////////////////////////
int main(int argc, char *argv[])
{
unsigned int ciclo;        // Ciclo de vida actual

leerDatosIniciales(); //vienen por stdin
for(ciclo=0;ciclo<400;ciclo++)              // iteramos unas 400 veces
        {
        CicloVida();              // Calculamos un ciclo de vida
	actualizar_bits_extra();
        grabarMatriz(ciclo);  // Sacamos el resultado por stdout
        }

/*DespedidaYCierre();*/
return(0);
}











